package com.fjbdxk.beidou.bd;

import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import static com.fjbdxk.beidou.bd.PacketHeader.PACK_HEADER_SIZE;

/**
 * 处理与设备测点数据相关的操作，包括解析、存储和管理设备数据
 */
public class PacketData {

    final static int TAG_COUNT = 10; // 测点数量
    final static int BUF_SIZE = 21;  // 结构大小
    DeviceDataList deviceDataList;//用于存储多个设备数据集合
    List<PacketOption> listOpt;//存储解析出的选项数据列表

    static final TagConfig[] DEF_CONFIGS;//一个 TagConfig 数组，存储默认的测点配置。每个 TagConfig 对象的构造函数的参数定义了测点的特性
    static {// 初始化默认测点配置
        DEF_CONFIGS = new TagConfig[TAG_COUNT];
        DEF_CONFIGS[0] = new TagConfig(1, 1, 0);
        DEF_CONFIGS[1] = new TagConfig(1, 1, 0);
        DEF_CONFIGS[2] = new TagConfig(2, 0.01, 0);
        DEF_CONFIGS[3] = new TagConfig(2, 1, 0);
        DEF_CONFIGS[4] = new TagConfig(2, 0.1, 0);
        DEF_CONFIGS[5] = new TagConfig(2, 1, 0);
        DEF_CONFIGS[6] = new TagConfig(2, 0.01, 0);
        DEF_CONFIGS[7] = new TagConfig(2, 0.1, 0);
        DEF_CONFIGS[8] = new TagConfig(2, 0.1, 0);
        DEF_CONFIGS[9] = new TagConfig(4, 1, 0);
    }

    public final TagConfig[] configs;
    //初始化 PacketData 对象，并确保传入的标签配置数组符合预期的条件
    public PacketData(TagConfig[] configs) throws Exception {//接受一个 TagConfig 数组，确保传入的数组长度与 TAG_COUNT 匹配
        assert (configs.length == TAG_COUNT);
        this.configs = configs;
        int len = 0;
        for (TagConfig config : configs) {
            len += config.byteCount;
        }
        //计算所有测点配置的字节数之和，如果超出 BUF_SIZE，则抛出异常
        if (len > BUF_SIZE) {
            throw new Exception("定义的测点总字节数为 " + len + ", 超过了结构大小 " + BUF_SIZE + ".");
        }
    }

    /**
     * 进行报文解析
     * 解析输入的字节数组（原始报文）和报文头信息
     * @param pack 原始报文
     * @param header 解析好的报文头
     */
    //解析输入的字节数组和头信息，并将解析后的数据存储在 deviceDataList 中
    public void parseData(byte[] pack, PacketHeader header) {
        deviceDataList = new DeviceDataList();
        deviceDataList.parseData(pack, header);
        parseOption(pack, header);
    }
    //计算当前对象的字节数，包括所有标签配置的字节数以及可能的一个额外字节开销,返回计算的总字节数
    public int getByteCount() {
        int count = 0;
        for (TagConfig config : configs) {
            count += config.byteCount;
        }
        return 1 + count;
    }
    //从字节数组中解析选项信息并存储在一个列表中。它首先根据数据包的头部信息计算出时间基准和相关的数量，然后根据数据包的类型计算出正确的偏移量，最后循环解析每个选项并将其存储在 listOpt 中
    public void parseOption(byte[] pack, PacketHeader packetHeader) {
        Date dtBase = new Date(packetHeader.ldt * 1000);
        int dataNum = packetHeader.dataNum;
        int optNum = packetHeader.optNum;
        if (packetHeader.type == PacketHeader.TP.TP_2) {// 纯报警报文
            optNum = packetHeader.total;
        }
        if (optNum > 0) {
            listOpt = new LinkedList<>();
        }
        int offset = PACK_HEADER_SIZE;
        if (packetHeader.type == PacketHeader.TP.TP_0 || packetHeader.type == PacketHeader.TP.TP_1) {
            offset += getByteCount() * dataNum;
        }
        int buf_len = PacketOption.LEN;
        byte[] buf = new byte[buf_len];
        for (int i=0; i<optNum; ++i) {
            System.arraycopy(pack, offset, buf, 0, buf_len);
            PacketOption option = new PacketOption(buf, dtBase);
            listOpt.add(option);
            offset += buf_len;
        }
    }

    /**
     * 1 台设备（风机）测点集合
     */
    class DeviceSet{
        public final int index;  // 第1个字节表示的序号
        public final DeviceData[] dataArray = new DeviceData[TAG_COUNT];

        /**
         * 生成一台设备（风机）的测点数组
         * @param buf 本设备（风机）报文段
         */
        public DeviceSet(byte[] buf, long ldtBase, boolean timeBias) {
            assert buf.length == BUF_SIZE;
            index = buf[0];
            int offset = 1;
            for (int i=0; i<TAG_COUNT; ++i) {
                final TagConfig config = configs[i];
                int size = config.byteCount;
                int data = 0;
                for (int j=0; j<size; ++j) {
                    int b = Byte.toUnsignedInt(buf[offset + j]);
                    int left = 8 * (size - j - 1);
                    data += (b << left);
                }
                long ldt = ldtBase;
                if (timeBias) {
                    ldt += index;
                }
                dataArray[i] = new DeviceData(config, data, new Date(ldt * 1000));
                offset += config.byteCount;
            }
        }
    }

    /**
     * 存储多台设备的数据集合，
     * 10 个 20B 测点数据
     */
    class DeviceDataList {
        final static int MAX_SIZE = 10;
        final static int DATA_SIZE = 21;
        int count = 0;//当前设备集合的数量
        DeviceSet[] deviceSet;//存储设备集合的数组

        /**
         * 解析完整的北斗报文，将解析后的设备数据存储在 deviceSet 中,根据报文类型（TP_0 或 TP_1）解析相应的数据
         * @param pack 完整的北斗报文（2级 最长 229B）
         * @return
         */
        public void parseData(byte[] pack, PacketHeader packetHeader) {
            if (packetHeader.dataNum > 0 && packetHeader.dataNum <= MAX_SIZE) {
                count = packetHeader.dataNum;
                deviceSet = new DeviceSet[count];
                long ldt = packetHeader.ldt;
                int offset = PACK_HEADER_SIZE;
                for (int i = 0; i<packetHeader.dataNum; ++i) {
                    switch (packetHeader.type) {
                        case TP_0:
                        {
                            byte[] buf = new byte[BUF_SIZE];
                            System.arraycopy(pack, offset, buf, 0, BUF_SIZE);
                            deviceSet[i] = new DeviceSet(buf, ldt, false);
                        }
                            break;
                        case TP_1:
                        {
                            byte[] buf = new byte[BUF_SIZE];
                            System.arraycopy(pack, offset, buf, 0, BUF_SIZE);
                            deviceSet[i] = new DeviceSet(buf, ldt, true);
                        }
                            break;

                        default:
                            break;
                    }

                    offset += BUF_SIZE;
                }
            }
        }
    }
}
